#
# Copyright (c) 2023 supercell
#
# SPDX-License-Identifier: BSD-3-Clause
#
require "./block_syntax"

# Parses GitHub Alert blocks
#
# See also: https://docs.github.com/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts
module Luce
  class AlertBlockSyntax < BlockSyntax
    # Whether this alert ends with a lazy continuation line.
    #
    # The definition of lazy continuation lines:
    # https://spec.commonmark.org/0.30/#lazy-continuation-line
    @lazy_continuation = false
    @content_line_regexp = Regex.new(%q{>?\s?(.*)*})

    def pattern : Regex
      Luce.alert_pattern
    end

    def can_parse?(parser : BlockParser) : Bool
      pattern.matches?(parser.current.content)
    end

    def parse_child_lines(parser : BlockParser) : Array(Line)
      # Grab all of the lines that form the alert, stripping off the ">".
      child_lines = [] of Line
      @lazy_continuation = false

      until parser.done?
        line_content = parser.current.content.lstrip
        stripped_content = line_content.sub(/^>?\s*/, "")
        match = (stripped_content.empty? && !line_content.starts_with?(">")) ? nil : @content_line_regexp.match(stripped_content)
        unless match.nil?
          child_lines << Line.new(stripped_content)
          parser.advance
          @lazy_continuation = false
          next
        end

        last_line = child_lines.empty? ? Line.new("") : child_lines.last

        # A paragraph continuation is OK. This is content that cannot be parsed
        # as any other syntax except Paragraph, and it doesn't match the bar in
        # a Setext Header.
        # Because indented code blocks cannot interrupt paragraphs, a line
        # matched CodeBlockSyntax is also paragraph continuation text.
        other_matched = parser.block_syntaxes.find(&.can_parse?(parser))
        if (other_matched.is_a?(ParagraphSyntax) && !last_line.is_blank_line? && !Luce.code_fence_pattern.matches?(last_line.content)) ||
           (other_matched.is_a?(CodeBlockSyntax) && !Luce.indent_pattern.matches?(last_line.content))
          child_lines << parser.current
          @lazy_continuation = true
          parser.advance
        else
          break
        end
      end

      child_lines
    end

    def parse(parser : BlockParser) : Node?
      # Parse the alert type from the first line.
      # ameba:disable Lint/NotNil
      type = pattern.match(parser.current.content).not_nil![1].downcase
      parser.advance
      child_lines = parse_child_lines(parser)
      # Recursively parse the contents of the alert.
      children = BlockParser.new(child_lines, parser.document).parse_lines(
        # The setext heading underline cannot be a lazy continuation line in a
        # block quote.
        # https://spec.commonmark.org/0.30/#example-93
        disable_setext_heading: @lazy_continuation,
        parent_syntax: self)

      # Mapping the alert title text.
      type_text_map = {
        "note"      => "Note",
        "tip"       => "Tip",
        "important" => "Important",
        "caution"   => "Caution",
        "warning"   => "Warning",
      }
      title_text = type_text_map[type]
      title_element = Element.new("p", [Text.new(title_text)] of Node)
      title_element.attributes["class"] = "markdown-alert-title"
      element_class = "markdown-alert markdown-alert-#{type}"
      element = Element.new("div", [title_element] + children)
      element.attributes["class"] = element_class
      element
    end
  end
end
